<template>
	<el-row :gutter="10" style="background: #f0f3f5;height:100%;">
		<el-col :span="5" class="left_box">
			<el-card style="border-right: 1px solid #ebeef5; height: 100%;background: #ffffff;">
				<el-input v-model="search" placeholder="输入云手机名称或ID搜索" clearable @input="Get_search_Phones" />
				<div class="select-all">
					<el-checkbox :model-value="selectAllChecked" :indeterminate="selectIndeterminate"
						@change="toggleSelectAll">
						全选 ({{ selectedDevicesCountInCurrentType }}/{{ phoneList.length }})
					</el-checkbox>
					<el-button type="primary" size="small" :disabled="selectedDevicesCountInCurrentType < 2"
						@click="batchRenew">
						批量续费
					</el-button>
				</div>
				<div v-loading="loading" style="display: flex; flex-direction: column; height: 70vh;">
					<el-scrollbar style="flex: 1; min-height: 0;padding-right: 15px;">
						<el-card v-for="phone in phoneList" :key="phone.id" shadow="hover" class="mt-3"
							style="position: relative; min-height: 80px; padding:10px;border-radius: 15px; cursor: pointer;"
							@click="selectPhone(phone)">
							<div style="width:100%;display: flex;justify-content: space-between;align-items: center;">
								<div style="display: flex;align-items: center;">
									<el-checkbox v-model="phone.checked" @click="selectPhone(phone)"></el-checkbox>
									<div style="font-weight: bold; margin-left: 8px;">
										{{ phone.instance_name || `云机${phone.id}` }}
									</div>
								</div>
								<div>
									<el-button type="primary" size="small"
										:disabled="selectedDevicesCountInCurrentType > 1 || (selectedDevicesCountInCurrentType === 1 && !phone.checked)"
										@click.stop="renewPhone(phone)">续费</el-button>
								</div>
							</div>
							<div style="display: flex;justify-content: space-between;width: 100%;align-items: center;margin-bottom: 4px;">
								<div style="color: #909399;">ID: {{ phone.instances.name }}</div>
							</div>
							<div style="display: flex;justify-content: space-between;width: 100%;align-items: center;">
								<div :style="{ color: getRemainingDaysColor(phone) }">
									<CountdownComponent :expireTime="phone.expire_time" :is_trial="phone.is_trial" />
								</div>
							</div>
						</el-card>
					</el-scrollbar>
				</div>
			</el-card>
		</el-col>
		<el-col :span="19" style="background: #f0f3f5;padding:0">
			<el-card style="border-left: 1px solid #ebeef5; height: 100%;background: #ffffff">
				<div class="top-right-buttons" style="margin-bottom: 10px;">
					<el-checkbox :model-value="selectAllChecked_right" :indeterminate="selectIndeterminate_right"
						@change="toggleSelectAll_right">
						全选 ({{ selectedDevicesCountInCurrentType2 }}/{{ phoneList.length }})
					</el-checkbox>
					<div>
						<el-popover placement="bottom" trigger="click" popper-style="padding:0;">
							<template #reference>
								<el-button type="primary">批量操作</el-button>
							</template>
							<div class="fun_options">
								<p @click="restart_All_Phone">重启</p>
								<p @click="reset_All_Phone">重置</p>
								<p @click="shutdown_All_Phone">一键新机</p>
								<el-popover placement="left" trigger="click" width="auto">
									<template #reference>
										<p @click.stop="change_phone_type('all')">更换机型</p>
									</template>
									<template #default>
										<el-cascader-panel v-model="cascader_value" :options="phone_type_list" :props="props" @change="check_All_phone_type"/>
									</template>
								</el-popover>
							</div>
						</el-popover>
						<el-button @click.stop="router.push('/groupControl')">云机群控</el-button>
					</div>
				</div>
				<el-row class="main_container">
					<el-col v-for="phone in previewPhoneList" :key="phone.id" :span="2">
						<div class="compact-rectangle">
							<div style="display: flex;flex-direction: column; height: 100%;width:100%;">
								<div style="display: flex;align-items: center;justify-content: space-between;width:100%;height: 20px;"
									@click="selectPhone_right(phone)">
									<el-checkbox v-model="phone.checked"
										@click="selectPhone_right(phone)"></el-checkbox>
									<span>{{ phone.instance_name || `云机${phone.id}` }}</span>
								</div>
								<div class="compact-image-container" v-loading="phone.screen_shot_loading"
									element-loading-text="重启中,请稍等" element-loading-background="rgba(0, 0, 0, 0.5)">
									<img v-if="phone.screen_shot" @click="showPhoneOverlay(phone)"
										style="max-width: 100%; max-height: 100%;"
										:src="store.state.request_url+phone.screen_shot" alt="预览图"
										@error="handleImageError">
									<img v-else src="@/assets/phone_no_bg2.png" alt="Logo"
										style="max-width: 100%; max-height: 100%;">
								</div>
							</div>
						</div>
					</el-col>
					<el-col :span="2">
						<div class="compact-rectangle" @click="addNewPhone">
							<div class="compact-image-container">
								<div
									style="display: flex;flex-direction: column;align-items:center;align-content: center; ">
									<el-icon :size="20">
										<CirclePlus />
									</el-icon>
									<span>新增云手机</span>
								</div>
							</div>
						</div>
					</el-col>
				</el-row>
			</el-card>
		</el-col>
		<transition name="slide-fade">
			<div v-show="showOverlay" class="overlay-mask">
				<div class="overlay-container" :style="{ width: elementWidth}">
					<el-icon class="close-icon" @click="closeOverlay(false)">
						<Close />
					</el-icon>
					<div class="overlay-content">
						<input id="ime" ref="imeRef" style="position:absolute; top:0px;"
							:readOnly="!store.state.Phone_Local_keyboard_flag" />
						<div v-if="currentPhone.id" class="phone-display" v-loading="restartPhone_loading"
							:element-loading-text="restartPhone_loading_text"
							element-loading-background="rgba(0, 0, 0, 0.7)" @mouseover="hoverdada">
							<div :id="`phone-display-${currentPhone.instances.machine.ip}:${currentPhone.instances.adb_port}`"
								style="padding-top: 30%;">
							</div>
							<el-button v-if="showInfoToggle" class="info-toggle-btn" type="primary" circle
								@click="showInfoPanel = !showInfoPanel">
								<el-icon>
									<component :is="showInfoPanel ? ArrowRight : ArrowLeft" />
								</el-icon>
							</el-button>
						</div>
						<div class="phone-info" :class="{ 'hidden': !showInfoPanel }"
							:style="{ display:elementWidthflag==true?'block':'none' }">
							<h3 style="margin-top: 0">云机详细信息</h3>
							<div class="info-container">
								<div class="info-row">
									<span class="info-label">云机名称</span>
									<span
										class="info-value">{{ currentPhone.instance_name || `云机${currentPhone.id}` }}</span>
								</div>
								<div class="info-row">
									<span class="info-label">云机ID</span>
									<span class="info-value">{{ currentPhone.instances?.name || '-' }}</span>
								</div>
								<div class="info-row">
									<span class="info-label">到期时间</span>
									<span class="info-value">{{ formatDate(currentPhone.expire_time) }}</span>
								</div>
								<div class="info-row">
									<span class="info-label">剩余天数</span>
									<span class="info-value">
										<CountdownComponent :expireTime="currentPhone.expire_time"
											:is_trial="currentPhone.is_trial" />
									</span>
								</div>
								<div class="info-row">
									<span class="info-label">运行状态</span>
									<span class="info-value">
										<span class="status-tag"
											:class="currentPhone.status === 1 ? 'running' : 'stopped'">
											{{ currentPhone.status === 1 ? '运行中' : '已停止' }}
										</span>
									</span>
								</div>
								<div class="info-row">
									<span class="info-label">配置信息</span>
									<span class="info-value">
										{{ currentPhone.instances?.cpu_cores || '-' }}核{{ (currentPhone.instances?.memory || 0) / 1024 }}G
									</span>
								</div>
								<div class="action-row">
									<el-popover placement="left" trigger="click" width="auto">
										<template #reference>
											<el-button plain round v-if="currentPhone.instances?.is_realphone==1"
												type="primary" @click="change_phone_type('single')">更换机型</el-button>
										</template>
										<template #default>
											<el-cascader-panel v-model="cascader_value" :options="phone_type_list" :props="props" @change="check_phone_type" />
										</template>
									</el-popover>
									<el-button plain round type="warning" @click="restartPhone">重启</el-button>
									<el-button plain round type="danger" @click="resetPhone">重置</el-button>
									<el-button plain round type="success" @click="shutdownPhone">一键新机</el-button>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</transition>
	</el-row>
</template>

<script setup lang="ts">
	type Machine = {
		id : number;
		ip : string;
		cpu : string;
		cpu_core : number;
		memory : number;
		volume : number;
		status : number;
		created_at : string;
		updated_at : string;
		mac_addr : string;
		volume_device : string;
	};

	type Instance = {
		id : number;
		machine_id : number;
		mac : string;
		ip : string;
		product_id : number;
		package_id : number;
		created_at : string;
		updated_at : string;
		status : number;
		is_realphone : number;
		adb_port : number;
		adb_address : string;
		height : number;
		width : number;
		image : string;
		image_tag : string;
		index : number;
		state : number;
		cpu_cores : number;
		memory : number;
		storage : number;
		name : string;
		machine : Machine;
	};

	type Phone = {
		id : number;
		user_id : string;
		instance_id : number;
		start_time : string;
		expire_time : string;
		status : number;
		created_at : string;
		updated_at : string;
		order_id : number;
		image : string;
		image_tag : string;
		auto_renew : number;
		screen_shot : string;
		instance_name : string;
		instances : Instance;
		checked ?: boolean;
		is_trial ?: number;
		state : number;
		screen_shot_loading ?: boolean;
	};

	import { useRouter } from 'vue-router';
	const router = useRouter();
	import '../style/app.css';
	import defaultImage from '@/assets/phone_no_bg2.png';
	import { ref, Ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
	import { Close, CirclePlus, ArrowRight, ArrowLeft } from '@element-plus/icons-vue';
	import { StreamClientScrcpy } from '@/app/googDevice/client/StreamClientScrcpy';
	import CountdownComponent from '@/components/CountdownComponent.vue';
	import { KeyInputHandler, type KeyEventListener } from '@/app/googDevice/KeyInputHandler';
	import { ElMessage } from 'element-plus';
	import request from '@/utils';
	import { useStore } from 'vuex';
	const store = useStore();
	const elementWidth = ref('auto');
	const elementWidthflag = ref(true);
	const search = ref('');
	const viewMode = ref('card');
	const showOverlay = ref(false);
	const showInfoPanel = ref(true);
	const windowWidth = ref(window.innerWidth);
	const loading = ref(false);
	const restartPhone_loading = ref(false);
	const restartPhone_loading_text = ref('云手机重启中,请稍等');
	const phoneList = ref<Phone[]>([]);
	const previewPhoneList = ref<Phone[]>([]);
	const No_change_phone_list = ref<Phone[]>([]);
	const filter_phone_type_list = ref<Phone[]>([]); //根据类型过滤备选数组(is_realphone)
	const Phone_selectedMap = ref(new Map<number, boolean>());
	const Phone_selectedMap_right = ref(new Map<number, boolean>());
	const currentPhone = ref<Phone>({} as Phone);
	const hash = location.hash.replace(/^#!/, '');
	const parsedQuery = new URLSearchParams(hash);
	const selectAllChecked = ref(false);
	const selectAllChecked_right = ref(false);
	const selectIndeterminate = ref(false);
	const selectIndeterminate_right = ref(false);
	const close_out : any = ref(null);
	const warningMessage : any = ref(null);
	const ws : Ref<WebSocket | null> = ref(null);
	const StreamClientScrcpy_start : Ref<StreamClientScrcpy | null> = ref(null);
	const cascader_value = ref([]);
	const loading_phone_info : any = ref(null);
	const phone_type_list = ref([]);
	const fetchPhoneList_Interval = ref<NodeJS.Timeout | null>(null)
	const IsEnglish_Or_Chinese_input = ref(true);
	const props = { label: 'model', value: 'id', children: 'models' };
	const selectedDevicesCountInCurrentType = computed(() => phoneList.value.filter(phone => phone.checked).length);
	const selectedDevicesCountInCurrentType2 = computed(() => previewPhoneList.value.filter(phone => phone.checked).length);
	const handleImageError = (e : any) => {
		const target = e.target as HTMLImageElement;
		target.src = defaultImage;
	};
	const filteredDevices = computed(() => phoneList.value);
	const toggleSelectAll = (val : boolean) => {
		phoneList.value.forEach(phone => {
			phone.checked = val;
			Phone_selectedMap.value.set(phone.id, val);
		});
		selectAllChecked.value = val;
		selectIndeterminate.value = false;
	};
	const toggleSelectAll_right = (val : boolean) => {
		previewPhoneList.value.forEach(phone => {
			phone.checked = val;
			Phone_selectedMap_right.value.set(phone.id, val);
		});
		selectIndeterminate_right.value = false;
		selectAllChecked_right.value = val;
	};

	watch(phoneList, (newVal) => {
		if (newVal.length === 0) {
			selectAllChecked.value = false;
			selectIndeterminate.value = false;
			return;
		}
		const checkedCount = newVal.filter(phone => phone.checked).length;
		selectAllChecked.value = checkedCount === newVal.length;
		selectIndeterminate.value = checkedCount > 0 && checkedCount < newVal.length;
	}, { deep: true });

	watch(previewPhoneList, (newVal) => {
		if (newVal.length === 0) {
			selectAllChecked_right.value = false;
			selectIndeterminate_right.value = false;
			return;
		}
		const checkedCount = newVal.filter(phone => phone.checked).length;
		selectAllChecked_right.value = checkedCount === newVal.length;
		selectIndeterminate_right.value = checkedCount > 0 && checkedCount < newVal.length;
	}, { deep: true });

	const Get_search_Phones = () => {
		phoneList.value = filteredPhones.value;
	};

	const filteredPhones = computed(() => {
		const searchTerm = search.value.toLowerCase();
		return No_change_phone_list.value.filter(phone => {
			const hasInstanceName = phone.instance_name && phone.instance_name.toLowerCase().includes(searchTerm);
			const hasInstancesName = phone.instances && phone.instances.name && phone.instances.name.toLowerCase().includes(searchTerm);
			return hasInstanceName || hasInstancesName;
		});
	});

	const selectedPhones = computed(() => phoneList.value.filter(phone => phone.checked));
	const showInfoToggle = computed(() => windowWidth.value < 800);

	interface ApiResponse<T = any> {
		code : number;
		msg : string;
		data : T;
	}

	const fetchPhoneList = async () => {
		try {
			loading.value = true;
			const response = await request.get<ApiResponse<Phone[]>>('/containers/list');
			if (response.data.code === 200) {
				No_change_phone_list.value = response.data.data.map(phone => ({
					...phone,
					checked: false,
					screen_shot_loading: phone.state == 0 ? true : false
				}));
				phoneList.value = filteredPhones.value;
				previewPhoneList.value = response.data.data.map(phone => ({
					...phone,
					checked: Phone_selectedMap_right.value.get(phone.id) ?? false,
					screen_shot_loading: phone.state == 0 ? true : false
				}));
			} else {
				ElMessage.error(response.data.msg || '获取云手机列表失败');
			}
		} catch (error) {} finally {
			loading.value = false;
		}
	};

	const getRemainingDaysColor = (phone : any) : string => {
		const expireDate = new Date(phone.expireTime);
		const now = new Date();
		const diffTime = expireDate.getTime() - now.getTime();
		const days = Math.max(0, Math.ceil(diffTime / (1000 * 60 * 60 * 24)));
		if (days <= 1) return 'red';
		if (days <= 3) return 'orange';
		return '#909399';
	};

	const formatDate = (dateString : string) : string => {
		if (!dateString) return '';
		const date = new Date(dateString);
		if (isNaN(date.getTime())) return 'Invalid Date';
		return date.toLocaleString('zh-CN', {
			year: 'numeric',
			month: '2-digit',
			day: '2-digit',
			hour: '2-digit',
			minute: '2-digit',
			hour12: false,
			timeZone: 'Asia/Shanghai'
		}).replace(/\//g, '-');
	};

	const isSingleSelected = (phone : Phone) => selectedDevicesCountInCurrentType.value === 1 && phone.checked;

	const selectPhone = (phone : Phone) => {
		phone.checked = !phone.checked;
		Phone_selectedMap.value.set(phone.id, !phone.checked);
	};

	const selectPhone_right = (phone : Phone) => {
		phone.checked = !phone.checked;
		Phone_selectedMap_right.value.set(phone.id, !phone.checked);
	};

	const showPhoneOverlay = async (phone : any) => {
		await new Promise<void>((resolve) => {
			currentPhone.value = phone;
			resolve();
		});
		const index = previewPhoneList.value.findIndex(item => item.id === phone.id);
		if (phone.state == 0) {
			previewPhoneList.value[index].screen_shot_loading = true;
		} else if (phone.state == 1) {
			initPhone(phone, 800);
		} else {
			previewPhoneList.value[index].screen_shot_loading = false;
			ElMessage.error('该云手机目前处于离线状态');
		}
		showInfoPanel.value = windowWidth.value >= 800;
	};

	const isUserActive = ref(true);
	const hoverdada = () => {
		if (restartPhone_loading_text.value == '断流待机中' && restartPhone_loading.value) {
			close_out.value = null;
			restartPhone_loading.value = false;
			initPhone(currentPhone.value, 800);
		}
	};

	const handleActivityChange = (active : boolean) => {
		isUserActive.value = active;
		if (!active) {
			restartPhone_loading.value = true;
			restartPhone_loading_text.value = '断流待机中';
			closeOverlay(true);
		}
	};

	watch(() => store.state.Horizontal_screen, (newvalue) => {
		if (newvalue) {
			document.documentElement.style.setProperty('--zoomsize', '0.6');
		} else {
			document.documentElement.style.setProperty('--zoomsize', '0.4');
		}
	});

	const initPhone = (phone : any, time : any) => {
		if (!phone.instances.machine || !phone.instances.adb_port) {
			ElMessage.error('该云手机实例信息不完整');
			return;
		}
		const udid = `${phone.instances.machine.ip}:${phone.instances.adb_port}`;
		const wsUrl = `wss://ws.jtcloud.icu:10443/?action=proxy-adb&remote=tcp:8886&udid=${udid}`;
		ws.value = new WebSocket(wsUrl);
		ws.value.onopen = () => { console.log('WebSocket连接成功'); };
		parsedQuery.set("action", "stream");
		parsedQuery.set("player", "mse");
		parsedQuery.set("udid", udid);
		parsedQuery.set("hostname", "ws.jtcloud.icu");
		parsedQuery.set("ws", wsUrl);
		parsedQuery.set("port", '10443');
		parsedQuery.set("secure", "true");
		StreamClientScrcpy_start.value = StreamClientScrcpy.start(parsedQuery);
		StreamClientScrcpy_start.value.setIdleThreshold(240000);
		StreamClientScrcpy_start.value.addActivityListener(handleActivityChange);
		const player = StreamClientScrcpy_start.value?.getPlayer();
		const checkbox = document.getElementById(`input_capture_keyboard_${udid}_${player?.getName()}`) as HTMLInputElement | null;
		if (checkbox) {
			checkbox.checked = false;
			checkbox.dispatchEvent(new Event('change'));
		}
		StreamClientScrcpy_start.value?.setHandleKeyboardEvents(false);
		setTimeout(() => {
			const toggleBtn = document.getElementById(`input_capture_keyboard_${udid}_${player?.getName()}`);
			if (toggleBtn) {
				toggleBtn.dispatchEvent(new MouseEvent('click', {
					bubbles: true,
					cancelable: true
				}));
			}
		}, 800);
		if (player) {
			setTimeout(() => {
				if (player.videoWidth > player.videoHeight) {
					store.commit('change_screen', true);
					document.documentElement.style.setProperty('--zoomsize', '0.6');
				} else {
					store.commit('change_screen', false);
					document.documentElement.style.setProperty('--zoomsize', '0.4');
				}
				showOverlay.value = true;
			}, time);
			player.touchableCanvas.onclick = () => {
				setTimeout(() => {
					if (!store.state.Horizontal_screen && player.videoWidth > player.videoHeight) {
						store.commit('change_screen', true);
					}
					if (store.state.Horizontal_screen && player.videoWidth < player.videoHeight) {
						store.commit('change_screen', false);
					}
				}, 1000);
			};
		}
	};

	const change_Local_keyboard_flag = (udid_ip : any) => {
		const requestData = { udid: udid_ip, type: 'local' };
		request.post('/tools/switch_local_keyboard', requestData).then(() => { });
	};
	//关闭云手机渲染操作弹窗
	const closeOverlay = (flag : any) => {
		if (flag == false) {
			showOverlay.value = false;
			cascader_value.value = [];
		}
		if (ws.value) {
			if (ws.value.readyState === WebSocket.OPEN) {
				ws.value.close(1000, '正常关闭');
			}
			ws.value = null;
		}
		if (StreamClientScrcpy_start.value) {
			warningMessage.value?.close();
			StreamClientScrcpy_start.value.onDisconnected();
			StreamClientScrcpy_start.value.removeActivityListener(handleActivityChange);
			StreamClientScrcpy_start.value.destroy();
			StreamClientScrcpy_start.value = null;
			store.commit('change_screen', false);
		}
		clearImeEvents();
	};
	//单个云手机续费
	const renewPhone = (Phone : any) => {
		const skip_value = { deviceId: [Phone.id],is_realphone:Phone.instances.is_realphone,tab: 'second' };
		router.push('/purches/' + encodeURIComponent(JSON.stringify(skip_value)));
	};
	//批量续费云手机
	const batchRenew = () => {
		const selected = phoneList.value.filter(phone => phone.checked);
		if (selected.length === 0) {
			ElMessage.warning('请先选择云手机');
			return;
		}
		const skip_value = { deviceId: selected.map(phone => phone.id),is_realphone:phoneList.value[0].instances.is_realphone, tab: 'second' };
		router.push('/purches/' + encodeURIComponent(JSON.stringify(skip_value)));
	};
	//购买云手机(跳转云手机页面)
	const addNewPhone = () => {
		router.push('/purches/0');
	};
	//重启云手机
	const restartPhone = () => {
		const requestData = { instance_id: currentPhone.value.id };
		const response = request.post('/containers/restart', requestData);
		const response2 = request.post('/containers/info', requestData);
		restartPhone_loading.value = true;
		restartPhone_loading_text.value = '云手机重启中,请稍等';
		closeOverlay(true);
		response.then(() => {
			var loading_val = setInterval(() => {
				response2.then((res2) => {
					if (res2.data.data.status == 1) {
						clearInterval(loading_val);
						setTimeout(() => {
							initPhone(currentPhone.value, 1000);
							restartPhone_loading.value = false;
						}, 6000);
					}
				});
			}, 1000);
		});
	};

	const shutdownPhone = async () => {
		const requestData = { instance_use_id: currentPhone.value.id };
		restartPhone_loading.value = true;
		restartPhone_loading_text.value = '云手机重启中,请稍等';
		closeOverlay(true);
		try {
			await request.post('/realphone/easy_recreate', requestData);
			await new Promise(resolve => setTimeout(resolve, 5000));
			await pollContainerInfo(currentPhone.value.id, {
				interval: 5000,
				maxAttempts: 10,
				onProgress: (attempt) => { console.log(`第${attempt}次轮询`); }
			});
			setTimeout(() => {
				initPhone(currentPhone.value, 1000);
			}, 6000);
			restartPhone_loading.value = false;
		} catch (error) {
			console.error('执行过程中出错:', error);
			restartPhone_loading.value = false;
			throw error;
		}
	};

	const restart_All_Phone = async () => {
		const selected_data = previewPhoneList.value.filter(phone => phone.checked);
		if (selected_data.length > 0) {
			const requestData = { instance_use_ids: selected_data.map(item => item.id) };
			request.post('/containers/batch_restart', requestData).then(() => {
				ElMessage.success('云手机重启中');
				fetchPhoneList();
			}).catch(() => { });
		} else {
			ElMessage.warning('请先勾选云手机');
		}
	};

	const reset_All_Phone = async () => {
		const selected_data = previewPhoneList.value.filter(phone => phone.checked);
		if (selected_data.length > 0) {
			const requestData = { instance_use_ids: selected_data.map(item => item.id) };
			request.post('/containers/batch_reset', requestData).then(() => {
				ElMessage.success('云手机重启中');
				fetchPhoneList();
			}).catch(() => { });
		} else {
			ElMessage.warning('请先勾选云手机');
		}
	};

	const shutdown_All_Phone = async () => {
		const selected_data = previewPhoneList.value.filter(phone => phone.checked);
		if (selected_data.length > 0) {
			const requestData = { instance_use_ids: selected_data.map(item => item.id) };
			request.post('/realphone/batch_easy_recreate', requestData).then(() => {
				ElMessage.success('云手机重启中');
				fetchPhoneList();
			}).catch(() => { });
		} else {
			ElMessage.warning('请先勾选云手机');
		}
	};

	const check_All_phone_type = async (value : any) => {
		const selected_data = previewPhoneList.value.filter(phone => phone.checked);
		if (selected_data.length > 0) {
			const requestData = {
				model_id: value[value.length - 1],
				instance_use_ids: selected_data.map(item => item.id)
			};
			restartPhone_loading.value = true;
			request.post('/realphone/batch_change_model', requestData).then(() => {
				ElMessage.success('云手机重启中');
				fetchPhoneList();
			}).catch(() => { });
		} else {
			ElMessage.warning('请先勾选云手机');
		}
	};

	const pollRequest = async <T>(
		requestFn : () => Promise<any>,
		checkFn : (response : any) => boolean,
		options : { interval ?: number; maxAttempts ?: number; onProgress ?: (attempt : number, response : any) => void; } = {}
	) : Promise<T> => {
		const { interval = 5000, maxAttempts = 10, onProgress } = options;
		let attempts = 0;
		const poll = async () : Promise<T> => {
			attempts++;
			try {
				const response = await requestFn();
				if (onProgress) onProgress(attempts, response);
				if (checkFn(response)) return response;
				else if (attempts >= maxAttempts) throw new Error(`轮询达到最大次数 (${maxAttempts})`);
				else {
					await new Promise(resolve => setTimeout(resolve, interval));
					return poll();
				}
			} catch (error) {
				if (attempts >= maxAttempts) throw error;
				else {
					await new Promise(resolve => setTimeout(resolve, interval));
					return poll();
				}
			}
		};
		return poll();
	};

	const pollContainerInfo = (
		instanceId : number,
		options : { interval ?: number; maxAttempts ?: number; onProgress ?: (attempt : number, response : any) => void; } = {}
	) => {
		const requestFn = () => request.post('/containers/info', { instance_id: instanceId });
		const checkFn = (response : any) => response.data.data.state === 1;
		return pollRequest(requestFn, checkFn, options);
	};

	const resetPhone = () => {
		ElMessage.info(`重置云手机 ${currentPhone.value.instance_name || currentPhone.value.id}`);
	};

	const change_phone_type = (type : any) => {
		request.get('/realphone/list').then((res) => {
			res.data.data.forEach((item : any, index : any) => {
				res.data.data[index] = { ...item, model: item.brand, id: index };
			});
			phone_type_list.value = res.data.data;
		});
	};

	const check_phone_type = async (value : any) => {
		var requestData = {
			model_id: value[value.length - 1],
			instance_use_id: currentPhone.value.id
		};
		restartPhone_loading.value = true;
		restartPhone_loading_text.value = '云手机重启中,请稍等';
		closeOverlay(true);
		try {
			await request.post('/realphone/change_model', requestData);
			await new Promise(resolve => setTimeout(resolve, 5000));
			await pollContainerInfo(currentPhone.value.id, {
				interval: 5000,
				maxAttempts: 10
			});
			setTimeout(() => {
				initPhone(currentPhone.value, 1000);
			}, 6000);
			restartPhone_loading.value = false;
		} catch (error) {
			restartPhone_loading.value = false;
			throw error;
		}
	};

	const polling_containers_info = () => {
		const containers_info = { instance_id: currentPhone.value.id };
		request.post('/containers/info', containers_info).then((res2) => {
			if (res2.data.data.status == 1) {
				clearInterval(loading_phone_info.value);
				setTimeout(() => {
					initPhone(currentPhone.value, 1000);
					restartPhone_loading.value = false;
				}, 5000);
			}
		});
	};

	const handleResize = () => {
		windowWidth.value = window.innerWidth;
		if (windowWidth.value >= 800 && !showInfoPanel.value) {
			showInfoPanel.value = true;
		}
		if (window.innerWidth < 800) {
			elementWidth.value = '100%';
			elementWidthflag.value = false;
		} else {
			elementWidth.value = 'auto';
			elementWidthflag.value = true;
		}
	};

	const initPlayers = async () => {
		const { MsePlayer } = await import('@/app/player/MsePlayer');
		StreamClientScrcpy.registerPlayer(MsePlayer);
		const { WebCodecsPlayer } = await import('@/app/player/WebCodecsPlayer');
		StreamClientScrcpy.registerPlayer(WebCodecsPlayer);
	};

	const prev = ref('');
	onMounted(async () => {
		await initPlayers();
		window.addEventListener('resize', handleResize);
		fetchPhoneList();
		fetchPhoneList_Interval.value=setInterval(() => { fetchPhoneList() }, 10000);
	});

	onUnmounted(() => {
		if (StreamClientScrcpy_start.value) {
			StreamClientScrcpy_start.value.removeActivityListener(handleActivityChange);
			StreamClientScrcpy_start.value.destroy();
		}
		// 清除定时器
		if (fetchPhoneList_Interval.value) {
		  clearInterval(fetchPhoneList_Interval.value);
		  fetchPhoneList_Interval.value = null;
		}
		window.removeEventListener('resize', handleResize);
		clearImeEvents();
	});

	interface CommitMsg { type : 'commit'; text ?: string; }
	interface DeleteMsg { type : 'delete'; count : number; text ?: string; }
	interface ReplaceMsg { type : 'replace'; text ?: string; }
	type InputMsg = CommitMsg | DeleteMsg | ReplaceMsg;
	const imeRef = ref<HTMLInputElement | null>(null);
	let lastSent = "";
	let isEventsBound = false;
	let keyInputListener : KeyEventListener | null = null;
	let isComposing = ref(false); // 改用ref，确保响应式（之前是普通变量，可能有延迟）
	function sendMsg(msg : InputMsg) : void {
		const currentPhone_udid = `${currentPhone.value.instances.machine.ip}:${currentPhone.value.instances.adb_port}`;
		const requestData = { udid: currentPhone_udid, text: msg.text };
		request.post('/containers/transfer_text', requestData).then(() => {
			if (!isComposing.value) {
				KeyInputHandler.setWebSocketEnabled(true);
			}
		}).catch(() => { });
	}
	// 初始化时设为false，避免初始状态异常
	const handleCompositionStart = (e : Event) => {
		const ev = e as CompositionEvent;
		isComposing.value = true;
		KeyInputHandler.setWebSocketEnabled(false);
		e.stopImmediatePropagation();
		e.preventDefault();
	};

	const handleCompositionEnd = (e : Event) => {
		const ev = e as CompositionEvent;
		const chineseText = ev.data?.trim();
		setTimeout(() => {
			isComposing.value = false;
			KeyInputHandler.setWebSocketEnabled(true);
		}, 100); // 延迟100ms，避免选字后立即触发英文输入

		if (chineseText && store.state.Phone_Local_keyboard_flag && imeRef.value) {
			const msg : CommitMsg = { type: 'commit', text: chineseText };
			sendMsg(msg);
			imeRef.value.value = "";
			lastSent = "";
		}
	};

	// 修改 handleInput - 处理英文字符输入
	const handleInput = (e : Event) => {
		if (!store.state.Phone_Local_keyboard_flag || !imeRef.value) return;
		const inputEvent = e as InputEvent;
		const newValue = imeRef.value.value;

		// 如果是组合输入期间，忽略
		if (isComposing.value) {
			return;
		}

		// 检测新增的字符并发送
		if (newValue.length > lastSent.length) {
			const newChars = newValue.slice(lastSent.length);

			// 发送每个新增的字符
			for (const char of newChars) {
				// 👇 关键：单独处理 ? 和 /，用文本传输接口发送
				if (char === '?' || char === '/') {
					sendSymbolAsText(char); // 直接发文本，不发按键事件
					continue; // 跳过后续的按键事件发送
				}
				// 只发送可打印字符，避免控制字符
				if (char >= ' ' && char <= '~') {
					KeyInputHandler.sendCharacter(char);
					console.log('前端传递字符到 sendCharacter:', char); // 加日志验证
				}
			}
			lastSent = newValue;
		}
		// 处理删除（但退格键已经在 keydown 中处理了）
		else if (newValue.length < lastSent.length) {
			lastSent = newValue;
		}
	};
	// 👇 新增：直接发送 ?/ 到云手机输入框（复用中文输入的文本接口）
	const sendSymbolAsText = (symbol : string) => {
		if (!currentPhone.value?.instances?.machine || !currentPhone.value?.instances?.adb_port) {
			ElMessage.error('云手机信息不完整，无法发送符号');
			return;
		}
		// 复用你中文输入时用的 transfer_text 接口（和后端约定好的文本传输接口）
		const currentPhoneUdid = `${currentPhone.value.instances.machine.ip}:${currentPhone.value.instances.adb_port}`;
		const requestData = {
			udid: currentPhoneUdid,
			text: symbol // 直接传 ? 或 / 符号
		};
		// 发送请求（和你中文输入的接口一致）
		request.post('/containers/transfer_text', requestData)
			.then(() => {})
			.catch(() => {});
	};

	// 修改 handleKeyDown 方法，添加 CapsLock 处理
	const handleKeyDown = (e : KeyboardEvent) => {
		if (!store.state.Phone_Local_keyboard_flag || isComposing.value) return;

		// 👇 新增：处理 CapsLock 键按下
		if (e.key === 'CapsLock') {
			e.preventDefault(); // 阻止浏览器默认行为（避免本地输入法干扰）
			KeyInputHandler.sendKeyEvent(e.code, 'down', 0); // metaState=0（无需修饰键）
			return;
		}

		// 原有退格键处理
		if (e.key === 'Backspace') {
			e.preventDefault();
			KeyInputHandler.sendBackspaceKey();
			return;
		}

		// 其他功能键（保留原有）
		const functionKeys = [
			'Enter', 'Tab', 'Escape', 'ArrowUp', 'ArrowDown',
			'ArrowLeft', 'ArrowRight', 'Delete', 'Home', 'End'
		];

		if (functionKeys.includes(e.key)) {
			e.preventDefault();
			const metaState = e.shiftKey ? 1 : 0;
			KeyInputHandler.sendKeyEvent(e.code, 'down', metaState);
		}
	};

	// 修改 handleKeyUp 方法，添加 CapsLock 处理
	const handleKeyUp = (e : KeyboardEvent) => {
		if (isComposing.value || !store.state.Phone_Local_keyboard_flag) {
			e.stopPropagation();
			e.preventDefault();
			return;
		}

		// 👇 新增：处理 CapsLock 键释放
		if (e.key === 'CapsLock') {
			e.preventDefault();
			KeyInputHandler.sendKeyEvent(e.code, 'up', 0); // metaState=0
			return;
		}

		// 其他功能键（保留原有）
		const functionKeys = [
			'Enter', 'Tab', 'Escape', 'ArrowUp', 'ArrowDown',
			'ArrowLeft', 'ArrowRight', 'Delete', 'Home', 'End'
		];

		if (functionKeys.includes(e.key)) {
			e.preventDefault();
			const metaState = e.shiftKey ? 1 : 0;
			KeyInputHandler.sendKeyEvent(e.code, 'up', metaState);
		}
	};
	const handlePaste = (e : Event) => {
		if (!store.state.Phone_Local_keyboard_flag || !imeRef.value) return;
		const ev = e as ClipboardEvent;
		ev.preventDefault();
		const pastedText = ev.clipboardData?.getData('text');
		if (pastedText) {
			const msg : CommitMsg = { type: 'commit', text: pastedText };
			sendMsg(msg);
			imeRef.value.value = "";
		}
	};


	const handleFocus = (e : Event) => {
		if (!store.state.Phone_Local_keyboard_flag || !imeRef.value) return;
		lastSent = imeRef.value.value;
	};

	const handleBlur = (e : Event) => {
		isComposing.value = false;
		KeyInputHandler.setWebSocketEnabled(true);
	};
	const bindImeEvents = () => {
		const inp = imeRef.value;
		if (!inp || !store.state.Phone_Local_keyboard_flag || isEventsBound) return;
		inp.addEventListener('input', handleInput);
		inp.addEventListener('keydown', handleKeyDown); // 捕获阶段拦截
		inp.addEventListener('compositionstart', handleCompositionStart);
		inp.addEventListener('compositionend', handleCompositionEnd);
		inp.addEventListener('keyup', handleKeyUp);
		inp.addEventListener('paste', handlePaste);
		inp.addEventListener('focus', handleFocus);
		inp.addEventListener('blur', handleBlur);
		keyInputListener = {
			onKeyEvent: (message) => {
				if (ws.value && ws.value.readyState === WebSocket.OPEN) {
					ws.value.send(JSON.stringify(message));
				}
			}
		};
		KeyInputHandler.addEventListener(keyInputListener);
		isEventsBound = true;
		inp.focus();
		setTimeout(() => inp.focus(), 100);
	};
	const clearImeEvents = () => {
		const inp = imeRef.value;
		if (!inp || !isEventsBound) return;
		inp.removeEventListener('keydown', handleKeyDown);
		inp.removeEventListener('input', handleInput);
		inp.removeEventListener('compositionstart', handleCompositionStart);
		inp.removeEventListener('compositionend', handleCompositionEnd);
		inp.removeEventListener('keyup', handleKeyUp);
		inp.removeEventListener('paste', handlePaste);
		inp.removeEventListener('focus', handleFocus);
		inp.removeEventListener('blur', handleBlur);
		if (keyInputListener) {
			KeyInputHandler.removeEventListener(keyInputListener);
			keyInputListener = null;
		}

		isEventsBound = false;
		isComposing.value = false;
		lastSent = "";
		inp.value = "";
	};

	watch(() => showOverlay.value, (isShow) => {
		if (isShow && store.state.Phone_Local_keyboard_flag) {
			bindImeEvents();
		} else {
			clearImeEvents();
		}
	}, { immediate: true });

	watch(() => store.state.Phone_Local_keyboard_flag, (isEnabled) => {
		const inp = imeRef.value;
		if (!inp) return;
		if (isEnabled && showOverlay.value) {
			bindImeEvents();
		} else {
			inp.value = "";
			isComposing.value = false;
			KeyInputHandler.setWebSocketEnabled(true);
		}
	}, { immediate: true });
</script>

<style lang="less" scoped>
	.fun_options {
		p {
			margin: 0;
			padding: 10px 0 10px 20px;

			&:hover {
				background-color: rgb(235.9, 245.3, 255);
				color: #409eff;
				cursor: pointer;
			}
		}
	}

	:deep(.function_btns .el-descriptions-item__content) {
		margin-left: 0 !important;
		padding-left: 0 !important;
	}

	.function_btns {
		display: flex;
		gap: 10px;
		flex-wrap: wrap;
	}

	:deep(.el-descriptions__body) {
		--el-descriptions-item-bordered-label-width: 0px;
	}

	.main_container {
		width: 100%;
		display: grid;
		grid-template-columns: repeat(8, 11.5%);
		justify-content: space-between;
		column-gap: 0.5%;
		row-gap: 4%;

		>div {
			min-width: 100%;
			height: 150px;

			img {
				width: 100%;
				height: auto;
				object-fit: contain;
			}
		}
	}

	/deep/ .phone-display {
		zoom: var(--zoomsize);
	}

	:deep(.phone-display .el-loading-spinner) {
		zoom: 2.4;
	}

	:deep(.more-box) {
		zoom: 2.4;
	}

	.left_box {
		background: #f0f3f5;

		:deep(.el-card__body) {
			padding: 10px;
		}
	}

	.select-all {
		margin-top: 10px;
		display: flex;
		justify-content: space-between;
		align-items: center;
	}

	.compact-rectangle .logo {
		font-size: 20px;
		font-weight: bold;
		padding: 20px;
	}

	.header-bar {
		display: flex;
		justify-content: space-between;
		align-items: center;
		background: #f5f7fa;
		padding: 10px 20px;
		border-bottom: 1px solid #ebeef5;
	}

	.top-right-buttons {
		display: flex;
		justify-content: space-between;
		align-items: center;
		gap: 10px;
	}

	.text-center {
		text-align: center;
		margin-top: 10px;
	}

	.mt-3 {
		margin-top: 15px;
	}

	.overlay-mask {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		background-color: rgba(0, 0, 0, 0.5);
		z-index: 2000;
		display: flex;
		justify-content: flex-end;
	}

	.overlay-container {
		height: 100%;
		background: white;
		position: relative;
	}

	.close-icon {
		position: absolute;
		top: 20px;
		right: 20px;
		font-size: 24px;
		cursor: pointer;
		z-index: 2001;
	}

	.overlay-content {
		display: flex;
		height: 100%;
	}

	.phone-display {
		flex: 1;
		padding: 20px;
		background: #f5f7fa;
		display: flex;
		align-items: center;
		justify-content: center;
		position: relative;
	}

	/deep/ .device-view {
		display: flex !important;
		flex-direction: row-reverse;
		flex-wrap: nowrap !important;
	}

	/deep/ .control-buttons-list {
		svg {
			width: 84px;
			height: 84px;

			&:hover {
				cursor: pointer;
			}
		}
	}

	.phone-info {
		width: 350px;
		background: #fff;
		border-radius: 4px;
		padding: 0;
		overflow: hidden;
		margin: 0 20px;

		h3 {
			margin: 0;
			padding: 16px;
			color: #303133;
			font-size: 16px;
			font-weight: 600;
			border-bottom: 1px solid #dcdfe6;
		}
	}

	.info-container {
		display: flex;
		flex-direction: column;
	}

	.info-row {
		display: flex;
		align-items: stretch;
		border-bottom: 1px solid #dcdfe6;

		&:last-child {
			border-bottom: none;
		}
	}

	.info-label {
		width: 100px;
		padding: 12px;
		color: #606266;
		font-size: 14px;
		font-weight: 500;
		flex-shrink: 0;
		background-color: #f5f7fa;
		border-left: 1px solid #dcdfe6;
		border-right: 1px solid #dcdfe6;
		display: flex;
		align-items: center;
	}

	.info-value {
		flex: 1;
		padding: 12px;
		color: #303133;
		font-size: 14px;
		display: flex;
		align-items: center;
		border-right: 1px solid #dcdfe6;
	}

	.status-tag {
		display: inline-block;
		padding: 0 10px;
		height: 24px;
		line-height: 24px;
		font-size: 12px;
		border-radius: 4px;
		box-sizing: border-box;
		white-space: nowrap;

		&.running {
			background-color: #f0f9eb;
			color: #67c23a;
			border: 1px solid #e1f3d8;
		}

		&.stopped {
			background-color: #fef0f0;
			color: #f56c6c;
			border: 1px solid #fde2e2;
		}
	}

	.action-row {
		box-sizing: border-box;
		padding: 10px 0;
		border-left: 1px solid #dcdfe6;
		border-right: 1px solid #dcdfe6;
		border-bottom: 1px solid #dcdfe6;
	}

	.action-buttons {
		padding: 16px;
		display: flex;
		gap: 10px;
		flex-wrap: wrap;
	}

	@media (max-width: 800px) {
		.phone-info {
			position: absolute;
			right: 0;
			top: 0;
			bottom: 0;
			width: 350px;
			background: white;
			box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
			transform: translateX(0);
			z-index: 1;

			&.hidden {
				transform: translateX(100%);
			}
		}

		.info-row {
			flex-direction: column;
			align-items: flex-start;
			gap: 4px;
		}

		.info-label {
			width: 100%;
		}

		.action-row {
			flex-direction: column;
		}

		.action-row .el-button {
			width: 100%;
			margin-bottom: 8px;
		}
	}

	@media (max-width: 500px) {
		.phone-display {
			min-width: 100%;
		}
	}

	.info-toggle-btn {
		position: absolute;
		top: 50%;
		right: 10px;
		transform: translateY(-50%);
		z-index: 2;
	}

	.slide-fade-enter-active {
		transition: all 0.3s ease-out;
	}

	.slide-fade-leave-active {
		transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
	}

	.slide-fade-enter-from,
	.slide-fade-leave-to {
		opacity: 0;
		transform: translateX(100%);
	}

	.compact-container {
		display: flex;
		gap: 4px;
		padding: 0 10px;
	}

	.compact-wrapper {
		width: 50%;
		padding: 0 2px;
	}

	.compact-rectangle {
		width: 100%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		margin: 0;
		background: #ffffff;
		border-radius: 8px;
		border: 1px solid #ebeef5;
		box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
		cursor: pointer;
		transition: all 0.2s;
		overflow: hidden;
		padding: 10px;
		box-sizing: border-box;

		&:hover {
			transform: translateY(-2px);
			box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);
		}
	}

	.compact-image-container {
		width: 100%;
		display: flex;
		align-items: center;
		justify-content: center;
		overflow: hidden;
		background: #ffffff;
		padding: 0;
		margin: 0;

		img {
			width: 100%;
			height: 100%;
			object-fit: cover;
		}
	}

	.compact-label {
		padding: 6px;
		text-align: center;
		font-size: 12px;
		color: #333;
		border-top: 1px solid #ebeef5;
	}

	.compact-add-btn {
		width: 90%;
		padding: 8px 12px;
		font-size: 12px;
		color: #409EFF;
		border-color: #409EFF;
		background-color: #ffffff;

		&:hover {
			color: #ffffff;
			background-color: #409EFF;
		}
	}
</style>